home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 8: LINUX Games
/
Linux Cubed Series 8 - LINUX Games.iso
/
games
/
x11
/
rpg
/
crossfir.92
/
crossfir
/
crossfire-0.92.5
/
server
/
ericserver.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-07-24
|
32KB
|
1,293 lines
/*
* This file implements all of the goo on the server side for handling
* clients. It's got a bunch of global variables for keeping track of
* each of the clients.
*
* SWH sends a message, protecting against certain exceptions being thrown.
*
* InitConnection initializes the connection with the client, and sends
* the server's protocol version (the client sends one of these too)
*
* PlayerCmd executes commands from the player
*
* VersionCmd checks the version number of the client
*
* AddMeCmd adds the player to the game.
*
* KeyConversion/KeyPress/KeyRelease are deprecated.
*
* Examine,Apply,Move Cmd handle those things
*
* dropconnection destroys the connection from the client.
*
* init_ericserver reads in all of the bitmap information, and initializes
* lots of spoo.
*
* CmdMapping is the dispatch table for the server, used in HandleClient,
* which gets called when the client has input.
*
* esrv_remove_player removes a player (called from xfire sources)
*
* esrv_drawinfo sends drawing info to the client
*
* send_query asks the client to query the user
*
* esrv_print_msg draws a normal message on the client
*
* esrv_write_ch is deprecated
*
* esrv_foo and esrv_bar are for debugging and just ship across server
* messages
*
* esrv_update_stats sends a statistics update.
*
* blah blah blah more esrv_* commands
*
* esrv_send_face sends a face to a client if they are in pixmap mode
* nothing gets sent in bitmap mode.
*
* esrv_map_new starts updating the map
*
* esrv_map_clearcell clears a map cell
*
* esrv_map_setbelow allows filling in all of the faces for the map.
* if a face has not already been sent to the client, it is sent now.
*
* mapcellchanged, compactlayer, compactstack, perform the map compressing
* operations
*
* esrv_map_doneredraw finishes the map update, and ships across the
* map updates.
*
* esrv_map_scroll tells the client to scroll the map, and does similarily
* for the locally cached copy.
*/
#undef StupidSunHeaders
#include <global.h>
#include <sproto.h>
#undef HANDLE
#if ERIC_SERVER
#include <tcplib.h>
#include <xmalloc.h>
#include <libc.h>
#include <arglist.h>
#include <xfile.h>
/*#include <chain-hash.h>*/
#include <newclient.h>
extern FILE *logfile;
extern int color_pix;
#define MAX_BUF 256
#define ESRV_VERSION 1002
#endif /* ERICSERVER */
#define EPORT 17399 /* (ESRV)_26 = 82935 % 32768 = 17399 */
int eport = EPORT;
#if ERICSERVER
/* List of integer defined commands */
#define STRINGCOMMAND 0
enum CState { CState_Init,CState_CanAdd,CState_Dead };
#define MAXMAPCELLFACES 50
/* Absolute MAX = 16383 given current map compaction algorithm */
#define MAXFACENUM 5000
struct MapCell {
short faces[MAXMAPCELLFACES];
int count;
};
struct Map {
struct MapCell cells[11][11];
};
struct statsinfo {
/*
long hp,maxhp,sp,maxsp,str,Int,wis,dex,con,cha,exp,level,wc,ac,dam;
long armour,speed,food,weapon_sp;
*/
char *range,*title;
};
enum FaceSendMode { Send_Face_Pixmap, Send_Face_Bitmap, Send_Face_None };
typedef struct __ClientInfo {
enum CState state;
long client_id;
int sentscroll; /* have we sent a scroll for the map since the last
map update? */
struct Map lastmap;
char faces_sent[MAXFACENUM];
enum FaceSendMode facesendmode;
/* HashTable ks2kc,kc2ks;*/
struct statsinfo stats;
} ClientInfo;
typedef struct __FaceInfo {
char *name;
char *data;
char bitmapdata[3*24];
long datalen;
} FaceInfo;
/*************************
* Globals for this file *
*************************/
static ClientInfo *cinfo;
static TcpSocket *conns;
static int nconns;
static long totalclients = 0;
static FaceInfo faces[MAXFACENUM];
/* Send With Handling */
static void SWH(int cnum,ArgList msg)
{
if (cinfo[cnum].state == CState_Dead)
return;
WITH_HANDLING {
ArgList_send(conns[cnum],msg);
} HANDLE {
BEGIN_MATCH;
XMATCH(tcplib,WriteFailed) {
cinfo[cnum].state = CState_Dead;
} END_MATCH;
} END_HANDLING;
}
static void InitConnection(int cnum)
{
ArgList msg;
msg = ArgList_create();
ArgList_addLong(msg,STRINGCOMMAND);
ArgList_addString(msg,"version");
ArgList_addLong(msg,ESRV_VERSION);
SWH(cnum,msg);
ArgList_destroy(msg);
cinfo[cnum].state = CState_Init;
cinfo[cnum].facesendmode = Send_Face_Pixmap;
totalclients++;
cinfo[cnum].client_id = totalclients;
memset(&cinfo[cnum].lastmap,0,sizeof(struct Map));
memset(&cinfo[cnum].faces_sent,0,sizeof(cinfo[cnum].faces_sent));
memset(&cinfo[cnum].stats,0,sizeof(struct statsinfo));
#if 0
cinfo[cnum].ks2kc = CreateHashTable(-2000,NULL);
cinfo[cnum].kc2ks = CreateHashTable(-2000,NULL);
#endif
cinfo[cnum].state = CState_CanAdd;
}
static void PlayerCmd(ArgList msg, int cnum)
{
int count = ArgList_getLong(msg,2);
char *buf = ArgList_getString(msg,3);
object *pl=esrv_getopfromcid(cinfo[cnum].client_id);
if (count) {
pl->contr->count=count;
pl->contr->count_left=0;
}
/* The following should never happen with a proper or honest client.
* Therefore, the error message doesn't have to be too clear - if
* someone is playing with a hacked/non working client, this gives them
* an idea of the problem, but they deserve what they get
*/
if (pl->contr->state!=ST_PLAYING) {
new_draw_info_format(NDI_UNIQUE, 0,pl,
"You can not issue commands - state is not ST_PLAYING (%s)", buf);
return;
}
pl->contr->idle=0;
/* Something better needs to be done. Ideally, we shouldn't read the
* data from the socket unless we are sure the client has some to
* execute it.
*/
if (pl->speed_left<-1.0) {
LOG(llevError,"Player has negative time - shouldn't do command.\n");
}
/* In c_new.c */
execute_newserver_command(pl, buf);
/* Perhaps something better should be done with a left over count.
* Cleaning up the input should probably be done first - all actions
* for the command that issued the count should be done before any other
* commands.
*/
pl->contr->count_left=0;
pl->contr->count=0;
/* We really should check how much speed the player has left, and
* start doing something appropriate (like not executing the commands,
* or stop reading from the socket.
*/
}
static void ReplyCmd(ArgList msg, int cnum)
{
char *buf = ArgList_getString(msg,2);
object *pl=esrv_getopfromcid(cinfo[cnum].client_id);
LOG(llevDebug,"In ReplyCmd: got reply '%s'\n", buf);
/* This is to synthesize how the data would be stored if it
* was normally entered. A bit of a hack, and should be cleaned up
* once all the X11 code is removed from the server.
*
* We pass 13 to many of the functions because this way they
* think it was the carriage return that was entered, and the
* function then does not try to do additional input.
*/
sprintf(pl->contr->write_buf,":%s", buf);
pl->contr->writing = strlen(pl->contr->write_buf);
switch (pl->contr->state) {
case ST_PLAYING:
LOG(llevError,"Got reply message with ST_PLAYING input state\n");
break;
case ST_PLAY_AGAIN:
/* We can check this for return value (2==quit). Maybe we
* should, and do something appropriate?
*/
receive_play_again(pl, buf[0]);
break;
case ST_ROLL_STAT:
key_roll_stat(pl,buf[0]);
break;
case ST_CHANGE_CLASS:
key_change_class(pl, buf[0]);
break;
case ST_CONFIRM_QUIT:
key_confirm_quit(pl, buf[0]);
break;
case ST_CONFIGURE:
LOG(llevError,"In client input handling, but into configure state\n");
pl->contr->state = ST_PLAYING;
break;
case ST_GET_NAME:
receive_player_name(pl,13);
break;
case ST_GET_PASSWORD:
case ST_CONFIRM_PASSWORD:
receive_player_password(pl,13);
break;
/* Pausing should really be done by the client, not the server.
* but this is easy to handle here.
*/
case ST_MENU_MORE:
shop_listing_more(pl);
break;
#ifdef SIMPLE_PARTY_SYSTEM
case ST_GET_PARTY_PASSWORD: /* Get password for party */
receive_party_password(pl,13);
break;
#endif /* SIMPLE_PARTY_SYSTEM */
default:
LOG(llevError,"Unknown input state: %d\n", pl->contr->state);
}
}
static int getcnum(long client_id)
{
int i;
for(i=1;i<nconns;i++)
if (cinfo[i].client_id == client_id)
return i;
return -1;
}
static void VersionCmd(ArgList msg,int cnum)
{
if (ESRV_VERSION != ArgList_getLong(msg,2)) {
printf("Server, Client have different versions (%d,%ld)\n",
ESRV_VERSION,ArgList_getLong(msg,2));
cinfo[cnum].state = CState_Dead;
}
}
static void KeyConversionCmd(ArgList msg,int cnum)
{
long i,max;
long ks,kc;
max = ArgList_getLong(msg,2);
for(i=3;i<3+max;i+=2) {
ks = ArgList_getLong(msg,i);
kc = ArgList_getLong(msg,i+1);
#if 0
HashOverwrite(cinfo[cnum].ks2kc,&ks,sizeof(long),&kc,sizeof(long));
/* HashOverwrite(cinfo[cnum].kc2ks,&kc,sizeof(long),&ks,sizeof(long));*/
#endif
}
cinfo[cnum].state = CState_CanAdd;
}
int add_player();
static void AddMeCmd(ArgList msg,int cnum)
{
int cpix = color_pix;
ArgList omsg;
omsg = ArgList_create();
ArgList_addLong(omsg,STRINGCOMMAND);
color_pix = 1;
if (cinfo[cnum].state != CState_CanAdd ||
add_player("nowhere.bad.don't.use.this",
"someone",NULL,cinfo[cnum].client_id)) {
ArgList_addString(omsg,"addme_failed");
} else {
ArgList_addString(omsg,"addme_success");
}
SWH(cnum,omsg);
ArgList_destroy(omsg);
color_pix = cpix;
}
static void KeyPressCmd(ArgList msg,int cnum)
{
unsigned int keycode;
unsigned long keysym;
char key;
keycode = ArgList_getLong(msg,2);
keysym = ArgList_getLong(msg,3);
key = ArgList_getLong(msg,4);
handle_keypress(esrv_getopfromcid(cinfo[cnum].client_id),
keycode,keysym,key);
}
static void KeyReleaseCmd(ArgList msg,int cnum)
{
unsigned int keycode;
unsigned long keysym;
char key;
keycode = ArgList_getLong(msg,2);
keysym = ArgList_getLong(msg,3);
key = ArgList_getLong(msg,4);
handle_keyrelease(esrv_getopfromcid(cinfo[cnum].client_id),
keycode,keysym);
}
static void ExamineCmd(ArgList msg,int cnum)
{
long tag = ArgList_getLong(msg, 2);
esrv_examine_object(esrv_getopfromcid(cinfo[cnum].client_id), tag);
}
static void ApplyCmd(ArgList msg,int cnum)
{
long tag = ArgList_getLong(msg, 2);
esrv_apply_object(esrv_getopfromcid(cinfo[cnum].client_id), tag);
}
static void MoveCmd(ArgList msg,int cnum)
{
long to = ArgList_getLong(msg, 2);
long tag = ArgList_getLong(msg, 3);
long nrof = ArgList_getLong(msg, 4);
printf ("Move item %ld (nrof=%ld) to %ld.\n", tag, nrof, to);
esrv_move_object(esrv_getopfromcid(cinfo[cnum].client_id), to, tag, nrof);
}
static void readbufline(char *buf,int size,FILE *in)
{
buf[0] = '\0';
fgets(buf,size,in);
buf[size-1] = '\0';
if (buf[0] == '\0')
return;
if (buf[strlen(buf)-1] != '\n') {
fprintf(stderr,"whoa, line '%s' not newline terminated??\n",buf);
abort();
}
}
static void dropconnection(int which)
{
int j;
CloseConnection(conns[which]);
if (cinfo[which].stats.range)
free(cinfo[which].stats.range);
if (cinfo[which].stats.title)
free(cinfo[which].stats.title);
#if 0
DestroyHashTable(cinfo[which].ks2kc,NULL);
DestroyHashTable(cinfo[which].kc2ks,NULL);
#endif
nconns--;
for(j=which;j<nconns;j++) {
conns[j] = conns[j+1];
cinfo[j] = cinfo[j+1];
}
}
#endif /* if ericserver */
long esrv_ks2kc(long client_id,long keysym)
{
#if ERICSERVER
int cnum;
if ((cnum = getcnum(client_id))<0) {
fprintf(logfile,"client %ld is gone.\n",client_id);
return 0;
}
#if 0
return *(long *)HashLookup(cinfo[cnum].ks2kc,&keysym,sizeof(long));
#endif
#endif
return 0;
}
void esrv_send_face(long client_id,short face_num);
void init_ericserver()
{
#if ERICSERVER
char filename[400];
char buf[500];
char *databuf,*cur,*end;
FILE *infile;
int num,len,comp;
LOG(llevDebug,"Initialize new client/server data\n");
nconns = 1;
conns = xmalloc(sizeof(TcpSocket));
conns[0] = BecomeServer(eport);
/* Read in the pixmaps file */
sprintf(filename,"%s/esrv_xpm.eric",LIBDIR);
infile = xfopen(filename,"r");
databuf = 0;
end = 0;
while(1) {
readbufline(buf,500,infile);
if (*buf == '\0')
break;
if(strncmp(buf,"ESRV_XPM ",9)!=0 ||
buf[14] != ' ') {
fprintf(stderr,"whoa, bad esrv_xpm line; not ESRV_XPM ...\n%s",buf);
abort();
}
num = atoi(buf+9);
if (num<0 || num>=MAXFACENUM) {
fprintf(stderr,"whoa, pixmap num %d \\not\\in 0..%d\n%s",
num,MAXFACENUM,buf);
abort();
}
buf[strlen(buf)-1] = '\0';
if (faces[num].name != NULL) {
fprintf(stderr,"whoa, pixmap #%d duplicated??\n",num);
abort();
}
faces[num].name = xmalloc(strlen(buf+15)+1);
strcpy(faces[num].name,buf+15);
cur = databuf;
/* Collect all the data for this pixmap */
while(1) {
readbufline(buf,500,infile);
if (*buf == '\0') {
fprintf(stderr,"whoa, pixmap #%d not terminated??\n",num);
abort();
}
if (strcmp(buf,"ESRV_XPM_END\n")==0)
break;
len = strlen(buf);
if (cur+len > end) {
long tmp = cur - databuf;
long tmp2 = end - databuf;
databuf = xrealloc(databuf,tmp2+10000);
cur = databuf+tmp;
end = databuf+tmp2+10000;
}
strcpy(cur,buf);
cur += len;
}
/* Collected all the data, put it into the pixmap buffer */
faces[num].data = xmalloc(cur-databuf+1);
faces[num].datalen = cur-databuf+1;
memcpy(faces[num].data, databuf,cur-databuf);
faces[num].data[cur-databuf] = '\0';
}
xfclose(infile);
free(databuf);
/* Assume bitmap information parallel to pixmap information */
sprintf(filename,"%s/%s.cfb",LIBDIR,FONTNAME);
if ((infile = open_and_uncompress(filename,0,&comp))==NULL) {
LOG(llevError,"Can't open %s file",filename);
abort();
}
for(num=0;num<MAXFACENUM;num++) {
if (faces[num].name == NULL)
break; /* Last one -- assumes pixmaps are contiguous. */
if (fread(faces[num].bitmapdata, 24 * 3, 1, infile) != 1) {
printf("Unable to read bitmap data for face #%d\n",num);
abort();
}
}
while(num<MAXFACENUM) {
if (faces[num].name != NULL) {
printf("Non-contiguous faces, %d sits in middle of nowhere.\n",num);
abort();
}
num++;
}
close_and_delete(infile, comp);
#endif
}
/* Either keep this near the start or end of the file so it is
* at least reasonablye easy to find.
*/
#if ERICSERVER
struct CmdMapping {
char *cmdname;
void (*cmdproc)(ArgList,int);
};
static struct CmdMapping commands[] = {
{ "version", VersionCmd },
{ "addme", AddMeCmd },
{ "keyconversion", KeyConversionCmd },
{ "keypress", KeyPressCmd },
{ "keyrelease", KeyReleaseCmd },
{ "examine", ExamineCmd },
{ "apply", ApplyCmd },
{ "move", MoveCmd },
{ "reply", ReplyCmd},
{ "command", PlayerCmd},
};
#define NCOMMANDS (sizeof(commands)/sizeof(struct CmdMapping))
static void HandleClient(int cnum)
{
TcpSocket conn = conns[cnum];
ArgList msg;
int i;
char *cmd;
msg = ArgList_receive(conn);
if (ArgList_getLong(msg,0)!= STRINGCOMMAND) {
printf("Bad message from client (%ld)\n",ArgList_getLong(msg,0));
exit(1);
}
cmd = ArgList_getString(msg,1);
for(i=0;i < NCOMMANDS;i++) {
if (strcmp(cmd,commands[i].cmdname)==0) {
commands[i].cmdproc(msg,cnum);
break;
}
}
if (i == NCOMMANDS) {
printf("Bad command from client (%s)\n",cmd);
}
ArgList_destroy(msg);
}
#endif
void esrv_quit_player();
void doeric_server()
{
#if ERICSERVER
volatile int i;
long cid;
for(i=1;i<nconns;i++) {
if (cinfo[i].state == CState_Dead) {
cid = cinfo[i].client_id;
dropconnection(i);
esrv_quit_player(cid);
i--;
}
}
WaitForInput(conns,nconns,0);
if (HasInput(conns[0])) {
printf("New Connection\n");
conns = xrealloc(conns,sizeof(TcpSocket)*(nconns+1));
cinfo = xrealloc(cinfo,sizeof(ClientInfo)*(nconns+1));
conns[nconns] = AcceptConnection(conns[0],NULL);
nconns++;
InitConnection(nconns-1);
}
for(i=1;i<nconns;i++) {
if (HasInput(conns[i])) {
WITH_HANDLING {
HandleClient(i);
} HANDLE {
BEGIN_MATCH
XMATCH(tcplib,NothingRead) {
} XMATCH(tcplib,ReadError) {
} XMATCH(tcplib,WriteFailed) {
} END_MATCH;
cid = cinfo[i].client_id;
dropconnection(i);
esrv_quit_player(cid);
i--;
} END_HANDLING;
}
}
#endif
}
int ericfd()
{
#if ERICSERVER
printf("Called ericfd()\n");
return GetTcpSocketFD(conns[0]);
#else
return 0;
#endif
}
void esrv_remove_player(long client_id)
{
#if ERICSERVER
int i;
for(i=0;i<nconns;i++) {
if (cinfo[i].client_id == client_id) {
cinfo[i].state = CState_Dead;
break;
}
}
#endif
}
void esrv_drawinfo(long client_id,const char *str)
{
#if ERICSERVER
int cnum;
ArgList msg;
if ((cnum = getcnum(client_id))<0) {
fprintf(logfile,"client %ld is gone.\n",client_id);
return;
}
msg = ArgList_create();
ArgList_addLong(msg,STRINGCOMMAND);
ArgList_addString(msg,"drawinfo");
ArgList_addChar(msg,NDI_BLACK);
ArgList_addString(msg,(char*)str);
SWH(cnum,msg);
ArgList_destroy(msg);
#endif
}
void send_query(long client_id, uint8 flags, char *text)
{
#if ERICSERVER
int cnum;
ArgList msg;
if ((cnum = getcnum(client_id))<0) {
fprintf(logfile,"client %ld is gone.\n",client_id);
return;
}
msg = ArgList_create();
ArgList_addLong(msg,STRINGCOMMAND);
ArgList_addString(msg,"query");
ArgList_addChar(msg, flags);
ArgList_addString(msg,text);
SWH(cnum,msg);
ArgList_destroy(msg);
#endif
}
void esrv_print_msg(long client_id,int color, char *str)
{
#if ERICSERVER
int cnum;
ArgList msg;
if ((cnum = getcnum(client_id))<0) {
fprintf(logfile,"client %ld is gone.\n",client_id);
return;
}
msg = ArgList_create();
ArgList_addLong(msg,STRINGCOMMAND);
ArgList_addString(msg,"drawinfo");
ArgList_addChar(msg,color);
ArgList_addString(msg,str);
SWH(cnum,msg);
ArgList_destroy(msg);
#endif
}
void esrv_write_ch(long client_id,unsigned char key)
{
#if ERICSERVER
#if 1 /* With the new input handling, this should not happen */
fprintf(stderr,"esrv_write_ch called\n");
return;
#else
int cnum;
ArgList msg;
if ((cnum = getcnum(client_id))<0) {
fprintf(logfile,"client %ld is gone.\n",client_id);
return;
}
msg = ArgList_create();
ArgList_addLong(msg,STRINGCOMMAND);
ArgList_addString(msg,"write_ch");
ArgList_addLong(msg,key);
SWH(cnum,msg);
ArgList_destroy(msg);
#endif
#endif
}
void esrv_foo(long client_id,char *foo)
{
#if ERICSERVER
int cnum;
ArgList msg;
if ((cnum = getcnum(client_id))<0) {
fprintf(logfile,"client %ld is gone.\n",client_id);
return;
}
msg = ArgList_create();
ArgList_addLong(msg,STRINGCOMMAND);
ArgList_addString(msg,"foo");
ArgList_addString(msg,foo);
/*SWH(cnum,msg);*/
ArgList_destroy(msg);
#endif
}
void esrv_bar(long client_id,char *foo)
{
#if ERICSERVER
int cnum;
ArgList msg;
if ((cnum = getcnum(client_id))<0) {
fprintf(logfile,"client %ld is gone.\n",client_id);
return;
}
msg = ArgList_create();
ArgList_addLong(msg,STRINGCOMMAND);
ArgList_addString(msg,"bar");
ArgList_addString(msg,foo);
/*SWH(cnum,msg);*/
ArgList_destroy(msg);
#endif
}
/* Sends the stats to the client - only sends them if they have changed */
#define AddIfLong(Old,New,Type) if (Old != New) {\
Old = New; \
ArgList_addChar(msg,Type);\
ArgList_addLong(msg,New);\
}
#define AddIfFloat(Old,New,Type) if (Old != New) {\
Old = New; \
ArgList_addChar(msg,Type);\
ArgList_addLong(msg,(long)(New*FLOAT_MULTI));\
}
#define AddIfString(Old,New,Type) if (Old == NULL || strcmp(Old,New)) {\
if (Old) free(Old);\
Old = strdup_local(New);\
ArgList_addChar(msg,Type);\
ArgList_addString(msg,New);\
}
void esrv_update_stats(long client_id, object *pl)
{
#if ERICSERVER
int cnum;
ArgList msg;
char buf[MAX_BUF];
if ((cnum = getcnum(client_id))<0) {
fprintf(stderr,"update_stats to not there person?\n");
return;
}
msg = ArgList_create();
ArgList_addLong(msg,STRINGCOMMAND);
ArgList_addString(msg,"stats");
AddIfLong(pl->contr->last_stats.hp, pl->stats.hp, CS_STAT_HP);
AddIfLong(pl->contr->last_stats.maxhp, pl->stats.maxhp, CS_STAT_MAXHP);
AddIfLong(pl->contr->last_stats.sp, pl->stats.sp, CS_STAT_SP);
AddIfLong(pl->contr->last_stats.maxsp, pl->stats.maxsp, CS_STAT_MAXSP);
AddIfLong(pl->contr->last_stats.grace, pl->stats.grace, CS_STAT_GRACE);
AddIfLong(pl->contr->last_stats.maxgrace, pl->stats.maxgrace, CS_STAT_MAXGRACE);
AddIfLong(pl->contr->last_stats.Str, pl->stats.Str, CS_STAT_STR);
AddIfLong(pl->contr->last_stats.Int, pl->stats.Int, CS_STAT_INT);
/* added this to allow Pow stat - b.t. */
AddIfLong(pl->contr->last_stats.Pow, pl->stats.Pow, CS_STAT_POW);
AddIfLong(pl->contr->last_stats.Wis, pl->stats.Wis, CS_STAT_WIS);
AddIfLong(pl->contr->last_stats.Dex, pl->stats.Dex, CS_STAT_DEX);
AddIfLong(pl->contr->last_stats.Con, pl->stats.Con, CS_STAT_CON);
AddIfLong(pl->contr->last_stats.Cha, pl->stats.Cha, CS_STAT_CHA);
AddIfLong(pl->contr->last_stats.exp, pl->stats.exp, CS_STAT_EXP);
AddIfLong(pl->contr->last_level, pl->level, CS_STAT_LEVEL);
AddIfLong(pl->contr->last_stats.wc, pl->stats.wc, CS_STAT_WC);
AddIfLong(pl->contr->last_stats.ac, pl->stats.ac, CS_STAT_AC);
AddIfLong(pl->contr->last_stats.dam, pl->stats.dam, CS_STAT_DAM);
AddIfLong(pl->contr->last_armour, pl->armour, CS_STAT_ARMOUR);
AddIfFloat(pl->contr->last_speed, pl->speed, CS_STAT_SPEED);
AddIfLong(pl->contr->last_stats.food, pl->stats.food, CS_STAT_FOOD);
AddIfFloat(pl->contr->last_weapon_sp, pl->contr->weapon_sp, CS_STAT_WEAP_SP);
rangetostring(pl, buf);
AddIfString(cinfo[cnum].stats.range, buf, CS_STAT_RANGE);
set_title(pl, buf);
AddIfString(cinfo[cnum].stats.title, buf, CS_STAT_TITLE);
if (ArgList_getLength(msg)>2) {
/* always has size 2 for stringcmd, "stats" */
printf("stats len %d\n",ArgList_getLength(msg));
SWH(cnum,msg);
}
ArgList_destroy(msg);
#endif
}
#if ERICSERVER
/*
* These are used to encode to simple commands, no need
* all overhead to ArfList handling
*
* Made cmd_buf to HUGE_BUF - this is needed for players that have a lot
* of stuff in their inventory (using MAX_BUF gets overflowed)
*/
static char cmd_buf[HUGE_BUF], *buf_ptr;
#define ADD_LONG(p,l) (*(p)++ = ((unsigned long)l) >> 24 & 0xFF,\
*(p)++ = ((unsigned long)l) >> 16 & 0xFF,\
*(p)++ = ((unsigned long)l) >> 8 & 0xFF,\
*(p)++ = ((unsigned long)l) & 0xFF)
#define ADD_STRING(p,s) (strcpy (p,s), p+=strlen(p)+1)
void esrv_send_simple_cmd(long client_id, char *cmd)
{
ArgList msg;
int cnum;
if ((cnum = getcnum(client_id))<0) {
fprintf(logfile,"client %ld is gone.\n",client_id);
return;
}
msg = ArgList_create();
ArgList_addLong(msg,STRINGCOMMAND);
ArgList_addString(msg, cmd);
ArgList_addBuf(msg, cmd_buf, buf_ptr - cmd_buf);
SWH(cnum,msg);
ArgList_destroy(msg);
}
#endif
void esrv_new_player(long client_id, long tag, char *name, long weight,
long face)
{
#if ERICSERVER
buf_ptr = cmd_buf;
ADD_LONG (buf_ptr, tag);
ADD_LONG (buf_ptr, weight);
ADD_LONG (buf_ptr, face);
ADD_STRING (buf_ptr, name);
esrv_send_simple_cmd (client_id, "player");
#endif
}
/*
* The next 3 functions must be called in the following order:
*
* esrv_new_location() starts making new item list
* esrv_add_item() adds new item to that list
* esrv_send_items() sends the item list to the client
*/
void esrv_new_location (long loc)
{
#if ERICSERVER
buf_ptr = cmd_buf;
ADD_LONG (buf_ptr, loc);
#endif
}
void esrv_add_item (long client_id, long tag, long flags, long weight,
long face, char *name)
{
#if ERICSERVER
int cnum;
if ((cnum = getcnum(client_id))<0) {
fprintf(logfile,"client %ld is gone.\n",client_id);
return;
}
if (! cinfo[cnum].faces_sent[face])
esrv_send_face(client_id, face);
ADD_LONG (buf_ptr, tag);
ADD_LONG (buf_ptr, flags);
ADD_LONG (buf_ptr, weight);
ADD_LONG (buf_ptr, face);
ADD_STRING (buf_ptr, name);
#endif
}
#if 0
void esrv_send_item(long client_id, long tag, long loc, char *name,
long weight, long face, long flags)
{
#if ERICSERVER
esrv_new_location (loc);
esrv_add_item (client_id, tag, flags, weight, face, name);
esrv_send_simple_cmd (client_id, "item");
#endif
}
#endif
void esrv_del_item(long client_id, long tag)
{
#if ERICSERVER
buf_ptr = cmd_buf;
ADD_LONG (buf_ptr, -1);
ADD_LONG (buf_ptr, tag);
esrv_send_simple_cmd (client_id, "item");
#endif
}
/* All the funky map routines */
#if ERICSERVER
static long map_client = -1;
static int map_cnum;
static struct Map newmap;
#endif
void esrv_send_face(long client_id,short face_num)
{
#if ERICSERVER
int cnum;
ArgList msg;
if ((cnum = getcnum(client_id))<0) {
fprintf(stderr,"send_face to not there person?\n");
return;
}
if (face_num < 0 || face_num >= MAXFACENUM) {
fprintf(stderr,"esrv_send_face(,%d) out of bounds??\n",face_num);
abort();
}
if (faces[face_num].data == NULL) {
fprintf(stderr,"faces[%d].data == NULL\n",face_num);
abort();
}
if (cinfo[cnum].facesendmode == Send_Face_Pixmap) {
msg = ArgList_create();
ArgList_addLong(msg,STRINGCOMMAND);
ArgList_addString(msg,"pixmap");
ArgList_addLong(msg,face_num);
ArgList_addBuf(msg,faces[face_num].data,faces[face_num].datalen);
SWH(cnum,msg);
ArgList_destroy(msg);
} else if (cinfo[cnum].facesendmode == Send_Face_Bitmap) {
} else if (cinfo[cnum].facesendmode == Send_Face_None) {
} else {
fprintf(stderr,"Invalid face send mode on cnum #%d\n",cnum);
abort();
}
/* printf("send_pixmap %d to %ld\n",face_num,client_id);*/
cinfo[cnum].faces_sent[face_num] = 1;
#endif
}
void esrv_map_new(long client_id)
{
#if ERICSERVER
if (map_client != -1) {
fprintf(stderr,"bad code -- didn't finish updating map\n");
return;
}
map_client = client_id;
map_cnum = getcnum(client_id);
memset(&newmap,0,sizeof(struct Map));
#endif
/* printf("NewMap for %ld\n",client_id);*/
}
void esrv_map_clearcell(long client_id,int x,int y)
{
#if ERICSERVER
if (client_id != map_client) {
fprintf(stderr,"bad user -- switched clientbeing updated\n");
abort();
}
if (x<0||x>10 ||y<0 ||y>10) {
fprintf(stderr,"bad user x/y not in 0..10\n");
abort();
}
newmap.cells[x][y].count = 0;
/* printf("ClearCell on %ld:(%d,%d)\n",client_id,x,y);*/
#endif
}
void esrv_map_setbelow(long client_id,int x,int y,short face_num)
{
#if ERICSERVER
if (client_id != map_client) {
fprintf(stderr,"bad user -- switched clientbeing updated\n");
abort();
}
if (x<0||x>10 ||y<0 ||y>10 || face_num < 0 || face_num > MAXFACENUM) {
fprintf(stderr,"bad user x/y/facenum not in 0..10,0..10,0..%d\n",
MAXFACENUM-1);
abort();
}
if(newmap.cells[x][y].count >= MAXMAPCELLFACES) {
fprintf(stderr,"whoa, too many faces\n");
abort();
}
newmap.cells[x][y].faces[newmap.cells[x][y].count] = face_num;
newmap.cells[x][y].count ++;
if (map_cnum>0 && cinfo[map_cnum].faces_sent[face_num] == 0)
esrv_send_face(client_id,face_num);
/* printf("SetBelow on %ld:(%d,%d; %d)\n",client_id,x,y,face_num);*/
#endif
}
struct LayerCell {
char xy;
short face;
};
struct MapLayer {
int count;
struct LayerCell lcells[121];
};
#if ERICSERVER
int mapcellchanged(int cnum,int i,int j)
{
int k;
if (cinfo[cnum].lastmap.cells[i][j].count != newmap.cells[i][j].count)
return 1;
for(k=0;k<newmap.cells[i][j].count;k++) {
if (cinfo[cnum].lastmap.cells[i][j].faces[k] !=
newmap.cells[i][j].faces[k]) {
return 1;
}
}
return 0;
}
unsigned char *compactlayer(int cnum,unsigned char *cur)
{
int i,j,k;
int face;
unsigned char *fcur;
struct MapLayer layers[MAXMAPCELLFACES];
for(k = 0;k<MAXMAPCELLFACES;k++)
layers[k].count = 0;
fcur = cur;
for(i=0;i<11;i++) {
for(j=0;j<11;j++) {
if (!mapcellchanged(cnum,i,j))
continue;
if (newmap.cells[i][j].count == 0) {
*cur = i*11+j;
cur++;
continue;
}
for(k=0;k<newmap.cells[i][j].count;k++) {
layers[k].lcells[layers[k].count].xy = i*11+j;
layers[k].lcells[layers[k].count].face =
newmap.cells[i][j].faces[k];
layers[k].count++;
}
}
}
if (fcur == cur && layers[0].count == 0)
return cur;
*cur = 255; /* mark end of explicitly cleared cells */
cur++;
for(k=0;k<MAXMAPCELLFACES;k++) {
if (layers[k].count == 0)
break; /* once a layer is entirely empty, no layer below it can
have anything in it either */
for(i=0;i<layers[k].count;) {
fcur = cur;
*cur = layers[k].lcells[i].face >> 8;
cur++;
*cur = layers[k].lcells[i].face & 0xFF;
cur++;
face = layers[k].lcells[i].face;
for(j=i;j<layers[k].count;j++) {
if (layers[k].lcells[j].face == face) {
*cur = layers[k].lcells[j].xy;
cur++;
layers[k].lcells[j].face = -1;
}
}
*(cur-1) = *(cur-1) | 128; /* mark for end of xy's; 11*11 < 128 */
while(i < layers[k].count &&
layers[k].lcells[i].face == -1)
i++;
}
*fcur = *fcur | 128; /* mark for end of faces at this layer */
}
return cur;
}
unsigned char *compactstack(int cnum,unsigned char *cur)
{
int i,j,k;
for(i=0;i<11;i++) {
for(j=0;j<11;j++) {
if (!mapcellchanged(cnum,i,j))
continue;
/* Pack in the x/y */
*cur = (i<<4)|j;
cur++;
/* Pack in a count */
*cur = (newmap.cells[i][j].count)&0xFF;
cur++;
for(k=0;k<newmap.cells[i][j].count;k++) {
*cur = (newmap.cells[i][j].faces[k] >> 8) & 0xFF;
cur++;
*cur = (newmap.cells[i][j].faces[k] & 0xFF);
cur++;
}
}
}
return cur;
}
#endif /* #if ERICSERVER */
void esrv_map_doneredraw(long client_id)
{
#if ERICSERVER
static long frames,bytes,tbytes,tframes;
int cnum;
unsigned char obuf[40000],*cur;
ArgList msg;
if (client_id != map_client) {
fprintf(stderr,"bad user -- switched clientbeing updated\n");
abort();
}
if ((cnum = getcnum(client_id))<0) {
fprintf(stderr,"whoa, updating map for non-existant client\n");
return;
}
#if 1
cur = compactlayer(cnum,obuf);
#else
cur = compactstack(cnum,obuf);
#endif
if (cur>obuf || cinfo[cnum].sentscroll) {
if (tframes>100) {
tframes = tbytes = 0;
}
tframes++;
frames++;
tbytes += (cur-obuf);
bytes += (cur-obuf);
#if 0
printf("ts%d,a%d;lf%ld,la%d\n",
cur-obuf,(int)((double)bytes/(double)frames +0.5),
tframes,(int)((double)tbytes/(double)tframes + 0.5));
#endif
memcpy(&cinfo[cnum].lastmap,&newmap,sizeof(struct Map));
msg = ArgList_create();
ArgList_addLong(msg,STRINGCOMMAND);
ArgList_addString(msg,"map");
ArgList_addBuf(msg,obuf,cur-obuf);
SWH(cnum,msg);
ArgList_destroy(msg);
cinfo[cnum].sentscroll = 0;
}
map_client = -1;
#endif
}
void esrv_map_scroll(long client_id,int dx,int dy)
{
#if ERICSERVER
int cnum;
ArgList msg;
struct Map newmap;
int x,y;
if ((cnum = getcnum(client_id))<0) {
fprintf(stderr,"whoa, updating map for non-existant client\n");
return;
}
msg = ArgList_create();
ArgList_addLong(msg,STRINGCOMMAND);
ArgList_addString(msg,"map_scroll");
ArgList_addLong(msg,dx);
ArgList_addLong(msg,dy);
SWH(cnum,msg);
ArgList_destroy(msg);
/* the x and y here are coordinates for the new map, i.e. if we moved
(dx,dy), newmap[x][y] = oldmap[x-dx][y-dy] */
for(x=0;x<11;x++) {
for(y=0;y<11;y++) {
newmap.cells[x][y].count = 0;
if (x+dx < 0 || x+dx >= 11)
continue;
if (y+dy < 0 || y+dy >= 11)
continue;
memcpy(&(newmap.cells[x][y]),
&(cinfo[cnum].lastmap.cells[x+dx][y+dy]),sizeof(struct MapCell));
}
}
memcpy(&(cinfo[cnum].lastmap), &newmap,sizeof(struct Map));
cinfo[cnum].sentscroll = 1;
#endif
}